昨天我們在 RN裡面使用 fetch來串接 API和後端溝通註冊。但其實在實際上使用 RN串接API的時候不會只用一個 fetch 直接來串接這樣。
會用一些狀態管理,Redux的概念來做著狀態的管理。但這樣又多了一個東西什麼是 Redux。
感覺又多介紹一個專有的東西怕嚇到人,但在我們本次挑戰之中,概念比實作重要。畢竟我們到現在還沒親自寫過什麼 Code🤣。
Redux 是一個專為管理應用程式狀態的 JavaScript 套。當開發應用程式時,常常會遇到跨多個元件或頁面分享和管理狀態的情況,這時 Redux 就能提供幫助。它的核心概念是將應用程式的狀態集中存放在一個「Store」(商店?[註一])中,並透過「Actions」(動作)和「Reducers」(篩子)來更新這個狀態。
其實我覺得 Redux有點複雜,新手在使用的時候也會不太清楚怎麼用。我當初其實是用了好一大段時間,也只是大概好像知道是怎麼一回事。但可以先做出來可以先動會再來了解我覺得也是一個不錯的學習方法。
我們來簡單看一下 Redux的概念。一個簡單的 UI 動作來說明 Redux 的工作流程,例如「按鈕點擊後改變畫面上的顏色」這個場景。
const changeColorAction = {
type: 'CHANGE_COLOR',
payload: 'blue',
};
這個 action 的 type
是「CHANGE_COLOR」,代表要改變顏色的一個事件,而 payload
裡面存放的是要改成的顏色「blue」。
dispatch
函數將這個 action 發送到 store:dispatch(changeColorAction);
Redux 的 dispatch
函數就像是一個foodpand 送餐員,將這個 changeColorAction
傳送到 store 中,讓 store的狀態改變
type
來決定如何改變 state:const initialState = { color: 'red' };
function colorReducer(state = initialState, action) {
switch (action.type) {
case 'CHANGE_COLOR':
return { ...state, color: action.payload };
default:
return state;
}
}
當 reducer 接收到 CHANGE_COLOR
的 action,它會將顏色更新為 action 中的 payload
值,這樣 state 中的顏色就會從綠色變成藍色。
Redux 的 store 會通知應用程式中使用該 state 的所有部分,然後 UI 會根據新的 state 做出變化,例如改變顏色:
const color = useSelector((state) => state.color);
return (
<div style={{ backgroundColor: color }}>
<button onClick={() => dispatch(changeColorAction)}>Change Color</button>
</div>
);
這樣,用戶每次點擊按鈕時,背景顏色都會根據更新後的 state 變成藍色。
註一:我以前看的書都說商店是 Store,不確定是不是因為我看的是中國的書的原因。
其實用來實作 Redux 有滿多工具的,常見的有 redux saga , redux toolkit ...等。也有比較簡單的配置直接用 redux thunk 之類的。
我這邊主要看過 Saga和 toolkit的實作。Saga我個人覺得滿複雜的,學習成本稍微高一點。Toolkit我覺得就很適合新手學習。程式碼也比較少一點比較簡單,我們這邊就選擇使用 redux toolkit實作。
雖然前面介紹了一堆概念,但我們其實只需要在 Cursor中和AI說請幫我加上 toolkit 的配置,應該就可以了,我們來試試看。
幫我加上 redux ,我想用 redux toolkit 這個套件。並且改寫註冊這個API
[!Info] GPT回應
要在你的 React Native 專案中加入 Redux Toolkit,並改寫註冊 API 的邏輯,你可以按照以下步驟進行:1. 安裝 Redux Toolkit 和 React-Redux
首先,你需要安裝
@reduxjs/toolkit
和react-redux
:yarn add @reduxjs/toolkit react-redux
2. 設定 Redux Store
在專案中創建一個新的文件夾
store
,並在裡面創建一個store.js
文件:import { configureStore } from '@reduxjs/toolkit'; import registerReducer from './registerSlice'; const store = configureStore({ reducer: { register: registerReducer, }, }); export default store;
3. 創建 Slice
在
store
文件夾中創建一個registerSlice.js
文件,並添加以下代碼:import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; export const registerUser = createAsyncThunk( 'register/registerUser', async (userData, { rejectWithValue }) => { try { const response = await fetch('http://localhost:3000/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(userData), }); if (!response.ok) { const data = await response.json(); return rejectWithValue(data.message || '註冊失敗,請稍後再試。'); } return await response.json(); } catch (error) { return rejectWithValue('註冊時發生錯誤,請檢查您的網路連接。'); } } ); const registerSlice = createSlice({ name: 'register', initialState: { error: null, success: false, }, reducers: { clearError: (state) => { state.error = null; }, }, extraReducers: (builder) => { builder .addCase(registerUser.fulfilled, (state) => { state.success = true; state.error = null; }) .addCase(registerUser.rejected, (state, action) => { state.error = action.payload; }); }, }); export const { clearError } = registerSlice.actions; export default registerSlice.reducer;
4. 更新 App 組件
在
App.tsx
中,將 Redux Store 包裹在應用中:import React from 'react'; import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { NativeBaseProvider } from 'native-base'; import { Provider } from 'react-redux'; import store from './store/store'; import Login from './components/Login'; import Register from './components/Register'; import Homepage from './components/Homepage'; import CreateReport from './components/CreateReport'; const Stack = createNativeStackNavigator(); const App = () => { return ( <Provider store={store}> <NativeBaseProvider> <NavigationContainer> <Stack.Navigator initialRouteName="Login"> <Stack.Screen name="Login" component={Login} /> <Stack.Screen name="Register" component={Register} /> <Stack.Screen name="Homepage" component={Homepage} /> <Stack.Screen name="CreateReport" component={CreateReport} /> </Stack.Navigator> </NavigationContainer> </NativeBaseProvider> </Provider> ); }; export default App;
5. 更新 Register 組件
最後,更新
Register.tsx
以使用 Redux Toolkit 的registerUser
:import React, { useState } from 'react'; import { NativeBaseProvider, Box, Input, Button, Text } from 'native-base'; import { Alert } from 'react-native'; import { useDispatch, useSelector } from 'react-redux'; import { registerUser, clearError } from '../store/registerSlice'; const Register = ({ navigation }) => { const dispatch = useDispatch(); const { error } = useSelector((state) => state.register); const [username, setUsername] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [account, setAccount] = useState(''); const handleRegister = () => { if (!username || !email || !password || !confirmPassword || !account) { Alert.alert('所有欄位都是必填的'); return; } if (password !== confirmPassword) { Alert.alert('密碼和確認密碼不一致'); return; } dispatch(registerUser({ username, email, password, account })); }; React.useEffect(() => { if (error) { Alert.alert('註冊失敗', error, [{ text: '確定', onPress: () => dispatch(clearError()) }]); } }, [error, dispatch]); return ( <Box flex={1} justifyContent="center" padding={4}> <Text fontSize="2xl" textAlign="center" marginBottom={4}>註冊</Text> <Input placeholder="使用者名字" value={username} onChangeText={setUsername} marginBottom={3} /> <Input placeholder="信箱" value={email} onChangeText={setEmail} marginBottom={3} /> <Input placeholder="密碼" type="password" value={password} onChangeText={setPassword} marginBottom={3} /> <Input placeholder="確認密碼" type="password" value={confirmPassword} onChangeText={setConfirmPassword} marginBottom={3} /> <Input placeholder="帳號" value={account} onChangeText={setAccount} marginBottom={3} /> <Button onPress={handleRegister}>註冊</Button> <Button onPress={() => navigation.navigate('Login')} marginTop={3}>返回登入</Button> </Box> ); }; export default Register;
完成
這樣就完成了 Redux Toolkit 的整合以及註冊 API 的改寫。現在你可以使用 Redux 來管理註冊的狀態和錯誤信息。
接著按下套用,Cusor 會幫你修改並套用。接著可以測試看看註冊頁面的行為是否一樣。
一樣可以成功註冊,查看資料庫也會有使用者資訊。
以前如果沒有AI,光自己讀文件到可以設定到可以動真的會需要花很多時間。然後就會放棄。畢竟設置的方法太多種,也常常卡在奇怪的地方做不出來真的會很挫折。
我們來看看 Cursor幫我們做了什麼。
首先使用 yarn add @reduxjs/toolkit react-redux
安裝了 Redux Toolkit 和 React-Redux的套件,最一開始沒安裝套件必須安裝。
接下來Cursor在 store.js
中設置了 Redux 的 store,並將我們即將創建的 registerSlice
寫在store裡面。configureStore
是 Redux Toolkit 提供的一個方法,用來簡化 store 的設置。
在這步中,我們創建了 registerSlice
,這是 Redux Toolkit 的功能,讓我們能夠將 reducer 和 action 一起處理。使用 createAsyncThunk
我們能夠處理 API 的異步請求,並在請求完成後自動更新狀態。
registerUser
action 發送異步的 API 請求。fulfilled
狀態會被觸發並更新應用的狀態;如果失敗,rejected
狀態會被觸發。我們將 Redux store 包在 App.tsx
中,這樣整個應用程式可以使用 Redux 的狀態管理功能。
在 Register.tsx
中,我們使用了 useDispatch
和 useSelector
來分發 registerUser
action 並讀取錯誤狀態。useEffect
會根據錯誤狀態來顯示錯誤提示或進行下一步操作。
雖然我們成功地請 Cursor幫我們把 redux-toolkit設置好了,也把註冊API功能改寫,但其實他寫的還是不夠好,我們明天會修正一下他寫的部分。還會介紹一下 Redux devTool如何使用。
又到了我們最後的工商時間啦~~~。Tom有在經營 Pocast,目前比較偏向職場類的閒聊,未來Podcast預計更新與程式軟體開發相關的主題。有興趣的朋友follow一下。
可以透過以下平台收聽: Apple podcast Spotify KKBOX
Linking Tree